package com.ody.util.code;

import com.odianyun.util.reflect.ReflectUtils;
import com.odianyun.util.string.StringPlaceHolderUtils;
import java.util.Map;
import java.util.Set;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;

public abstract class PlaceHolderHelper {
    private static ScriptEngine engine;
    
    static {
        ScriptEngineManager manager = new ScriptEngineManager();
        engine = manager.getEngineByName("groovy");
    }

	public static String replace(String s, Map<String, Object> map) {
        String _s = s;
		Set<String> placeHolders = StringPlaceHolderUtils.getPlaceHolders(s);
		if (placeHolders.isEmpty()) return s;
		
		for (String key : placeHolders) {
			Object value = map.get(key);
			if (value == null) {
				value = tryGetGroovyScript(key, map);
			}
			if (value != null){
				s = s.replace(StringPlaceHolderUtils.PLACE_HOLDER_PREFIX 
						+ key + StringPlaceHolderUtils.PLACE_HOLDER_SUFFIX, 
						value.toString());
			}
		}
		return s;
	}
    
    private static Object tryGetGroovyScript(String key, Map<String, Object> map) {
        ScriptContext ctx = new SimpleScriptContext();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            ctx.setAttribute(entry.getKey(), entry.getValue(), ScriptContext.ENGINE_SCOPE);
        }
        try {
            return engine.eval(key, ctx);
        } catch (Exception e) {
            return tryGetComplexValue(key, map);
        }
    }
	
	private static Object tryGetComplexValue(String key, Map<String, Object> map) {
		int idx = key.indexOf(".");
		if (idx != -1) {
			String prop = key.substring(0, idx);
			
			Object bean = map.get(prop);
			if (bean == null) return null;
			
			return getBeanValue(bean, key.substring(idx + 1));
		}
		return null;
	}
	
	private static Object getBeanValue(Object obj, String key) {
		int idx = key.indexOf(".");
		if (idx != -1) {
			String prop = key.substring(0, idx);
			Object subObj = ReflectUtils.callGetMethod(obj, prop);
			return getBeanValue(subObj, key.substring(idx + 1));
		}
		return ReflectUtils.callGetMethod(obj, key);
	}
}
